package com.rtos.model.process;

import com.rtos.common.ProcessStatus;
import com.rtos.model.cpu.CPU;
import com.rtos.model.cpu.registers.AX;
import com.rtos.model.cpu.registers.PC;
import com.rtos.model.device.Device;
import com.rtos.model.device.QueueItem;
import com.rtos.model.fileSystem.FileUtil;
import com.rtos.model.memory.MemoryDispatcher;
import com.rtos.model.memory.SystemMemory;
import com.rtos.model.process.queue.BlockingQueue;
import com.rtos.model.process.queue.ReadyQueue;
import com.rtos.model.process.queue.RunningQueue;
import com.rtos.utils.Util;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * @Author: biubiu0002
 * @Date: 2021-08-31 0:00
 * 单例
 **/
public class ProcessDispatcher {


    private static List<byte[]> execFiles = new ArrayList<>();
    private Device device = Device.getInstance();

    private Process hangoutProcess;


    private MemoryDispatcher memoryDispatcher = MemoryDispatcher.getInstance();
    private FileUtil fileUtil = FileUtil.getInstance();
    private static ProcessDispatcher processDispatcher;



    private ProcessDispatcher() {
        /**
         * 初始化 创建、加载闲逛进程
         */
        PCB pcb = new PCB();
        pcb.pidProperty().set(1);
        pcb.setProcessStatus(ProcessStatus.HANGOUT);
        pcb.setTotalRunTime(new SimpleIntegerProperty(0));
        pcb.setRestRunTime(new SimpleIntegerProperty(0));
        pcb.setLastRunTime(new SimpleIntegerProperty(0));
        pcb.setDeviceType(new SimpleStringProperty("无"));
        hangoutProcess = new Process(pcb, 0);
//        this.loadExeFiles();
        this.loadExecFilesFromFileSystem();

        /**
         * 闲逛进程要加入运行队列!
         */
        RunningQueue.add(hangoutProcess);

    }

    public Process readyProcessToRun() {
//        Process runningProcess = RunningQueue.getRunningProcess();
//        runningProcessToReady(runningProcess);
        //ReadyQueue弹出队头进程 可能为null
        Process popReadyProcess = ReadyQueue.pop();

        if (popReadyProcess == null) {
            popReadyProcess = hangoutProcess;
        }
        Process runningProcess = this.getRunningProcess();
        if (runningProcess == null) {
            RunningQueue.add(popReadyProcess);
        }
        else if(runningProcess==hangoutProcess){
            RunningQueue.remove();
            RunningQueue.add(popReadyProcess);
        }
        PCB pcb = popReadyProcess.getPcb();
        pcb.lastRunTimeProperty().set(0);

        return popReadyProcess;

    }

    public void runningProcessToReady(Process runningProcess) {
        if (runningProcess != hangoutProcess) {
            RunningQueue.remove();
            ReadyQueue.add(runningProcess);
        }
        else{
            System.out.println("不支持hangoutProcess进入runningProcessToReady()方法");

        }

    }

    public void runningProcessToBlock(Process process) {
        /**
         * 运行进程换下 加入阻塞队列
         */
        RunningQueue.remove();
        BlockingQueue.add(process);

    }

    public void blockingProcessToReady(Process process) {
        CPU cpu = CPU.getInstance();
        BlockingQueue.remove(process);
        int[] awake = processDispatcher.awake(process);
        if (awake != null) {
            cpu.deviceTime[awake[0] - 1] = awake[1];
        }

        /**
         * 是否为闲置进程
         */
        if (cpu.runningProcess != hangoutProcess) {
            ReadyQueue.add(process);
        } else {
            /**
             * 当前运行的为闲置进程
             * 先移除hangoutProcess
             * 再把process加入到运行队列
             *
             */
            PCB pcb = process.getPcb();
            pcb.lastRunTimeProperty().set(0);

            RunningQueue.remove();
            RunningQueue.add(process);
            //cpu reload
            cpu.reloadRegisters(process);
        }

    }

    public Process getRunningProcess() {
        /**
         *  hangoutProcess不存放在RunningQueue中
         */
        return RunningQueue.getFirst();
    }

    public Process getHangoutProcess() {
        return hangoutProcess;
    }


    public static ProcessDispatcher getInstance() {
        if (processDispatcher == null) {
            processDispatcher = new ProcessDispatcher();
        }
        return processDispatcher;
    }

    /**
     * 进程创建 create()，主要工作是：
     * 第一步，申请空白进程控制块；
     * 第二步，申请主存空间，申请成功，装入主存；
     * 第三步，初始化进程控制块；
     * 第四步，进程移入就绪队列
     */
    public synchronized Process create() {
        //设置进程使用的可执行文件
        byte[] instructions = execFiles.get(new Random().nextInt(execFiles.size()));
//        byte[] instructions = execFiles.get(1);
        int size = instructions.length;
        PCB pcb = new PCB();
        pcb.init(instructions);
        pcb.setExec(instructions);
        pcb.setSize(size);
        Process process = new Process(pcb);

        if (memoryDispatcher.alloc(process)) {
            ReadyQueue.add(process);
            return process;
        }

        return null;
    }

    public boolean create(Process process) {
        byte[] instructions = execFiles.get(new Random().nextInt(execFiles.size()));
        PCB pcb = new PCB();
        pcb.init(instructions);
        pcb.setExec(instructions);
        int size = instructions.length;
//        Process process = new Process(pcb,size);
        process.setExec(instructions);
        process.setPcb(pcb);
        //alloc memory
        if (memoryDispatcher.alloc(process)) {
            ReadyQueue.add(process);
            return true;
        }
        return false;
    }

    /**
     * 根据可执行文件中的指令创建进程
     *
     * @param instructions
     * @return
     */
    public boolean create(byte[] instructions) {
        PCB pcb = new PCB();
        pcb.init(instructions);
        pcb.setExec(instructions);
        int size = instructions.length;
        Process process = new Process(pcb, size);
        process.setExec(instructions);
        process.setPcb(pcb);
        //alloc memory
        if (memoryDispatcher.alloc(process)) {
            ReadyQueue.add(process);
            return true;
        }
        return false;
    }

    /**
     * 进程撤销 destroy()，主要工作是：
     * 第一步，回收进程所占内存；
     * 第二步，回收进程控制块；
     * 第三步，在屏幕上显示进程执行结果，进程撤销。
     */
    public synchronized void destory(Process process) {
        //回收内存
        //回收pcb
        RunningQueue.remove();
        memoryDispatcher.free(process);

    }

    /**
     * 进程阻塞 block()，主要工作是：
     * 第一步，保存运行进程的 CPU 现场；
     * 第二步，修改进程状态；
     * 第三步，将进程链入对应的阻塞队列，然后转向进程调度。
     */


    /**
     * @param reason
     * @return 占用的设备号 1~8 9
     */
    public int block(String reason) {
        int deviceIndex = 9;
        Process runningProcess = RunningQueue.getFirst();
        QueueItem queueItem = new QueueItem(String.valueOf(runningProcess.getPid().get()), runningProcess.getSize(), runningProcess.getPcb().getBlockingTime().get());
        if ("A".equals(reason)) {
           deviceIndex = device.applyA(queueItem);
        } else if ("B".equals(reason)) {
            deviceIndex = device.applyB(queueItem);
       } else if ("C".equals(reason)) {
            deviceIndex = device.applyC(queueItem);
       }
        /**
         * 运行进程换下 加入阻塞队列
         */

        this.runningProcessToBlock(runningProcess);
        return deviceIndex;
    }


    /**
     * 进程唤醒 awake()
     * 进程唤醒的主要工作是将进程由阻塞队列中摘下，修改进程
     * 状态为就绪，然后链入就绪队列。
     * 释放设备 下一个进程占用设备
     *
     * @param process
     * @return 下一个进程占用设备的设备号，使用时间
     */
    public int[] awake(Process process) {
        String reason = process.getPcb().deviceTypeProperty().get();
//        System.out.println(reason);
        int[] ints = new int[0];
        if ("A".equals(reason)) {
            ints = device.freeA(String.valueOf(process.getPid().get()));
        } else if ("B".equals(reason)) {
            ints = device.freeB(String.valueOf(process.getPid().get()));
        } else if ("C".equals(reason)) {
            ints = device.freeC(String.valueOf(process.getPid().get()));
        }
        return ints;
    }


//    从JavaIO里读 已废弃
//    由于pc buffer 容量是1byte 最大表示范围是-128~127 所以可执行文件行数不能超过127行 否则 (byte)(127+1)=-128 PC为负数
//    public void loadExeFiles() {
//        try {
//            URL resource = getClass().getResource("");
//            URI uri = resource.toURI();
//            File directory = new File(uri);
//            //FileNameFilter lambda表达式
//            File[] exeFiles = directory.listFiles((dir, name) -> name.endsWith(".exe"));
//            for(File f:exeFiles){
//                String line;
//                int lineCount=0;
//                int index=0;
//                BufferedReader br = new BufferedReader(new FileReader(f));
//                while((line=br.readLine())!=null){
//                    lineCount++;
//                }
//                br.close();
//                byte[] instructions = new byte[lineCount];
//                br = new BufferedReader(new FileReader(f));
//                while((line=br.readLine())!=null){
//                    instructions[index++] = Util.compile(line);
//                }
//                execFiles.add(instructions);
//            }
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }


    //从文件系统中读可执行文件 保存到list中
    public void loadExecFilesFromFileSystem(){
        try {
            ArrayList<String> exeFilePaths = fileUtil.getExeFiles();;
            System.out.println("exeFiles = " + exeFilePaths);
            for(String path:exeFilePaths){
                this.addExeFile(path);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public boolean addExeFile(String filePath){
        String fileContent = fileUtil.getFileContent(filePath);
//        System.out.println("文件内容为"+fileContent);
        String[] split = fileContent.split("\\n");
        int size = split.length;
        byte[] instructions = new byte[size];
        for(int i=0;i<size;i++){
            byte compile = Util.compile(split[i]);
            instructions[i] = compile;
        }
        execFiles.add(instructions);
        return true;
    }

    public boolean removeExeFile(String filePath){
        String fileContent = fileUtil.getFileContent(filePath);
        System.out.println("获取文件内容成功");
        String[] split = fileContent.split("\\n");
        int size = split.length;
        byte[] instructions = new byte[size];
        for(int i=0;i<size;i++){
            byte compile = Util.compile(split[i]);
            instructions[i] = compile;
        }
        if(execFiles.contains(instructions)){
            execFiles.remove(instructions);
        }
        return true;
    }

}
